package t.l;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import t.l.component.InfoLabel;
import t.l.component.Person;

public class Main implements FrameView {

  private RuntimeData runtimeData = null;

  private InfoLabel nInfoLabel, mInfoLabel, kInfoLabel, animIntervalInfoLabel,
      currentCountInfoLabel;

  private JButton saveToDiskBt, resetBt, unlockToResetBt, controlBt;

  private final File dataFile = new File("RuntimeData.dt");

  private MFrame mFrame;
  private MFrame.Controller controller;

  public static void main(String[] args) {
    Main main = new Main();
    main.init();
  }

  private void init() {
    double size = 600.0;
    double center = size / 2;
    initFrame(size, center);
  }

  private void initFrame(double size, double center) {
    nInfoLabel = new InfoLabel("n :", 5, "person");
    mInfoLabel = new InfoLabel("m :", 4, "");
    kInfoLabel = new InfoLabel("k :", 1, "");
    animIntervalInfoLabel = new InfoLabel("动画速度 :", 500, "ms");
    currentCountInfoLabel = new InfoLabel("当前报数 :", 0, "");
    currentCountInfoLabel.setValueTextEnabled(false);
    JPanel jPanel = new JPanel();
    jPanel.setLayout(new GridLayout(6, 1));
    jPanel.add(nInfoLabel);
    jPanel.add(kInfoLabel);
    jPanel.add(mInfoLabel);
    jPanel.add(animIntervalInfoLabel);
    jPanel.add(currentCountInfoLabel);

    mFrame = new MFrame(size + 400, size);
    controller = mFrame.getController();
    controller.setFrameView(this);

    controlBt = new JButton("start");
    saveToDiskBt = new JButton("save to disk");
    resetBt = new JButton("reset");
    unlockToResetBt = new JButton("unlock to reset");

    resetBt.addActionListener(e -> {
      resetPersons(nInfoLabel.getValue());
      currentCountInfoLabel.setValue(0);
    });

    unlockToResetBt.addActionListener(e -> {
      setModifyEnable(true);
      if (controller.isLoopStarted()) {
        controller.cancelLoop();
      }
    });

    controlBt.addActionListener(e -> {
      if (!controller.isLoopStarted()) {
        startLoopWithInit();
      } else if (!controller.isLooping()) {
        resumeLoop();
      } else {
        pauseLoop();
      }
    });

    saveToDiskBt.addActionListener(
        e -> controller.saveToDisk(nInfoLabel.getValue(), kInfoLabel.getValue(), dataFile));

    jPanel.add(controlBt);

    int valueLabelX = (int) (center + mFrame.getTableDiameter() / 2 + 100);
    int valueLabelY = 100;
    int valueLabelWidth = 180;
    int valueLabelHeight = 150;

    jPanel.setBounds(valueLabelX, valueLabelY, valueLabelWidth, valueLabelHeight);
    saveToDiskBt.setBounds((int) (center + mFrame.getTableDiameter() / 2 + 50), 400, 100, 50);
    resetBt.setBounds(valueLabelX + valueLabelWidth + 20, valueLabelY + 40, 70, 40);
    unlockToResetBt.setBounds(resetBt.getX(), resetBt.getY(), 140, resetBt.getHeight());
    mFrame.add(jPanel);
    mFrame.add(saveToDiskBt);
    mFrame.add(resetBt);
    mFrame.add(unlockToResetBt);
    setModifyEnable(false);
    mFrame.setVisible(true);
    mFrame.addWindowListener(new WindowAdapter() {
      @Override public void windowClosing(WindowEvent e) {
        super.windowClosing(e);
        System.exit(0);
      }
    });

    if (dataFile.exists()) {
      try {
        runtimeData = Util.gson.fromJson(new FileReader(dataFile), RuntimeData.class);
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      }
      if (runtimeData != null) {
        mInfoLabel.setValue(runtimeData.getM());
        nInfoLabel.setValue(runtimeData.getN());
        kInfoLabel.setValue(runtimeData.getK());
        animIntervalInfoLabel.setValue(runtimeData.getAnimateInterval());
        currentCountInfoLabel.setValue(runtimeData.getCurrentCount());
        resetPersons(runtimeData.getPersonNumList(), runtimeData.getRemovedNumList());
      } else {
        resetPersons(nInfoLabel.getValue());
      }
    } else {
      resetPersons(nInfoLabel.getValue());
    }
  }

  private void resetPersons(int n) {
    if (n < 1) {
      Util.toast("person number should be larger than zero", "Illegal input",
          JOptionPane.ERROR_MESSAGE);
      return;
    }
    List<Person> personList = new ArrayList<>();
    for (int i = 1; i <= n; i++) {
      personList.add(new Person(i));
    }
    setModifyEnable(false);
    mFrame.resetPersonList(personList);
  }

  private void resetPersons(List<Integer> personNumList, List<Integer> removedNumList) {
    setModifyEnable(false);
    if (Util.notEmpty(personNumList)) {
      List<Person> personList = new ArrayList<>();
      personNumList.forEach(integer -> personList.add(new Person(integer)));
      mFrame.resetPersonList(personList);
    } else {
      mFrame.resetPersonList(null);
    }
    if (Util.notEmpty(removedNumList)) {
      List<Person> removedList = new ArrayList<>();
      personNumList.forEach(integer -> removedList.add(new Person(integer)));
      mFrame.updateRemovedPerson(removedList);
    } else {
      mFrame.updateRemovedPerson(null);
    }
  }

  private void pauseLoop() {
    controller.pauseAnim();
  }

  private void startLoopWithInit() {
    controller.setM(mInfoLabel.getValue());
    if (runtimeData != null) {
      controller.setRuntimeK(runtimeData.getRuntimeK());
      runtimeData = null;
    } else {
      controller.setRuntimeK(kInfoLabel.getValue() - 1);
    }
    controller.setAnimInterval(animIntervalInfoLabel.getValue());
    controller.setCurrentCount(currentCountInfoLabel.getValue());
    controller.startLoop();
  }

  private void resumeLoop() {
    controller.resumeLoop();
  }

  private void setModifyEnable(boolean enable) {
    nInfoLabel.setValueTextEnabled(enable);
    kInfoLabel.setValueTextEnabled(enable);
    mInfoLabel.setValueTextEnabled(enable);
    animIntervalInfoLabel.setValueTextEnabled(enable);
    unlockToResetBt.setVisible(!enable);
    resetBt.setVisible(enable);
    controlBt.setEnabled(!enable);
  }

  @Override public void isLooping(boolean isLooping) {
    controlBt.setText(isLooping ? "pause" : "start");
    saveToDiskBt.setEnabled(!isLooping);
  }

  @Override public void loopAnimating(int currentCount) {
    currentCountInfoLabel.setValue(currentCount);
  }

  @Override public void endLoop() {
    saveToDiskBt.setVisible(true);
    setModifyEnable(true);
  }
}
